CognitoをプライベートなSAML IdPと連携してみた
CognitoはSAMLを使った外部IdP(Identity Provider)連携に対応しています。
弊社ブログでも、CognitoとSAMLの外部IdP連携について解説しているものがあります。
- [AWS CDK] ALBとCognitoを使ってOktaをIdPとするSAML認証をしてみた | DevelopersIO
- ADFSをSAML IdPとしてCognitoユーザープールと連携する | DevelopersIO
- Amazon Cognito user poolの外部IdPとしてAzure ADを設定してみた | DevelopersIO
SAMLは、SAML AssertionをSAML IdPとSP(Service Provider)間でやり取りし、IdP側でユーザー認証した結果をレスポンスします。
その際、SAML Assertionを検証するために、SP側(Cognito)にはIdPのメタデータ(公開鍵)が必要です。
Cognitoの場合、IdPのメタデータはURL形式で指定して自動取得するか、ファイルアップロードで設定できます。
メタデータの取得がファイルアップロードでできるので、理論上はCognitoと外部IdP間の通信ができなくても、SAML IdPとして連携できます。
この構成で本当に連携できるのか、実際に試してみました。
AWS構成図
こんな環境作っていきます。
Cognitoのログイン環境として、ALB+Cognitoを利用します。
ALB+Cognitoを組み合わせた認証については、弊社ブログで解説しているのでこちらをご覧ください。
この先、ALB+Cognito環境は構築済みであるという前提で進めます。
- EC2とALBでコンテンツを配信している環境にCognitoで認証を追加してみました | DevelopersIO
- インフラエンジニアが一切コードを書かずにWebサーバーに認証機能を実装した話 | DevelopersIO
そこに、外部IdPとしてローカル環境に構築した、SAML IdPを追加します。
ローカル環境につくることで、CognitoからSAML IdPへ通信できない状況を再現します。
検証用SAML IdP環境の構築
こちらのブログを参考にさせてもらい、 saml-idp
コマンドを利用してSAML IdP環境を構築します。
openssl
コマンドでIdP用の証明書ファイルを作成します。
Country Name(国名)等は検証用途なので適当に入力します。
$ openssl req -x509 -new -noenc \
-newkey rsa:2048 \
-keyout idp-private-key.pem \
-out idp-public-cert.pem \
-days 7300
証明書が作成できたら、 saml-idp
コマンドを使ってIdP環境を起動します。
$ export ENTITY_ID="<<CognitoのSP Entity ID>>"
$ export ACS_URL="<<CognitoのACS URL>>"
$ saml-idp \
--acs ${ACS_URL} \
--aud ${ENTITY_ID}
CognitoのSP Entity IDは、次のような形式です。
ユーザープールIDによって異なります。
urn:amazon:cognito:sp:<<ユーザープールID>>
CognitoのACS URLは、次のような形式です。
-
https://<<ユーザープールのドメイン名>>/saml2/idpresponse
起動できたら、ブラウザで http://localhost:7000
にアクセスします。
SAML IdPのメタデータはこちらからダウンロードできます。
これでSAML IdP環境側は準備完了です。
CognitoのSAML IdP連携設定
Cognitoの「サインインエクスペリエンス」タブから、SAML IdPの設定をします。
「アイデンティティプロバイダー」にSAMLを選択して、プロバイダー名を適当に入力します。
今回は saml-test
という名前にしました。
メタデータドキュメントのソースで「メタデータドキュメントをアップロード」を選択し、先ほどダウンロードしたメタデータファイルをアップロードします。
「SAMLプロバイダーとユーザープールの間で属性をマッピング」では、SAML IdPから返される属性をユーザープールの属性にマッピングします。
どういう属性が連携されるかは、SAML IdP側の仕様を確認してください。
saml-idp
の場合は、メタデータに記載されている属性が連携されます。
今回は、ユーザープールの email
属性に email
属性を、 ユーザープールの given_name
属性に displayName
属性をマッピングして連携するようにしてみます。
次にCognitoのアプリケーションの統合タブから、SAML IdP用のアプリケーションクライアントを作成します。
ALBでCognitoを使用するために必要な設定は割愛します。先ほど紹介したブログを参考にしてください。
このブログでは、SAML IdP連携で必要な設定のみ紹介します。
IDプロバイダーに先ほど設定したSAML IdP用のプロバイダー名を選択します。Cognitoユーザープールは選択から外します。
これでCognitoとSAML IdPの連携設定は完了です。
動作確認
実際にログインしてみます。
開発者ツールで記録を取りながら、ALBのドメインにアクセスすると、CognitoのHosted UIにリダイレクトされて、SAML IdPのログイン画面にリダイレクトされる動きが見えます。
想定通り動けばここに表示されている、Display NameとE-Mail AddressがCognitoに連携されるんだなーという気持ちで、Sign inボタンを押します。
そうすると、CognitoにSAMLレスポンスが渡って、ALB側にリダイレクトされてCognitoから認可コードを受け取り、ログインが完了して最終的にEC2のコンテンツが表示されます。
ログイン後に、Cognitoユーザープールのユーザーも見てみると、emailとDisplayNameがCognito側にも連携されていることが確認できます。
うまくいかない場合は、SAML IdP側の設定にも注意が必要です。
この辺がうまく設定できていないと連携に失敗します。
重要な注意点
SAMLメタデータには公開鍵が含まれており、公開鍵には有効期限があります。
通常であれば、有効期限前にSAML IdP側が秘密鍵と公開鍵を更新して、メタデータを更新することになります。
その場合、メタデータをアップロードしているCognito側もメタデータを手動で更新する必要があることに注意してください。
今回の構成の場合、SAML IdP側がプライベートネットワークにあるので不可能ですが、
SAML IdPにメタデータのパブリックエンドポイントがあればそちらを利用することを推奨しています。
パブリックエンドポイントがあれば、Cognitoが自動で更新してくれます。
おわりに
CognitoとSAML IdPの連携について、Cognito側からSAML IdP側へ通信ができなくても連携できることを確認しました。
ドキュメント読んだ限り、理論上はできそうだけど本当にできるのかなってところが確かめられて良かったです。
このブログがどなたかのお役に立てれば幸いです。